#include "common_modbus_485_port.h"
#include "common_list.h"
#include "common_modbus.h"

#include "../../bsp/modbus_485.h"

static int bus_485_init(struct xp_modbus_bus *bus)
{
    if (bus == NULL) return -1;
    struct modbus_485_attr *bus_attr = (struct modbus_485_attr *)(bus->private_data);
    xp_slist_init(&bus->dev_list);

    if (0 != xp_modbus_init(bus_attr->port, 2400 * bus_attr->baudrate, bus_attr->parity, 10 * bus_attr->timeout))
        return -1;

    return 0;
}

static int bus_485_read(xp_modbus_device_t *dev, unsigned short addr, unsigned short *data, int len, enum xp_modbus_func func)
{
    unsigned char rec_data[10] = {0};
    unsigned char rec_len = 0;
    unsigned char repeat_times = 5;
    int ret = 0;

    if (dev == NULL || data == NULL) return -1;

    unsigned char port = dev->bus->port;

    MB_data modbus = {0};

    switch (func)
    {
    case mb_read_coil_reg:
        modbus.mb_fun = mb_coil_r;
        break;
    case mb_read_single_reg:
        modbus.mb_fun = mb_hold_r;
        break;
    default:
        return -1;
        break;
    }
    modbus.slave_id = dev->sid;
    modbus.start_addr = addr;
    modbus.wBuf = data;
    modbus.rwLen = len;
    modbus.resBuf = rec_data;
    modbus.resLen = &rec_len;

    while (repeat_times--)
    {
        ret = xp_modbus_ask(port, &modbus);
        if (0 == ret)
            break;
    }

    if (0 != ret)
        return -1;
    memcpy(data, rec_data, len * 2);
    return 0;
}

static int bus_485_write(xp_modbus_device_t *dev, unsigned short addr, unsigned short *data, int len, enum xp_modbus_func func)
{
    unsigned char rec_data[10] = {0};
    unsigned char rec_len = 0;
    unsigned char repeat_times = 5;
    int ret = 0;
    
    if (dev == NULL || data == NULL) return -1;

    unsigned char port = dev->bus->port;

    MB_data modbus = {0};
    switch (func)
    {
    case mb_write_coil_reg:
        modbus.mb_fun = mb_coil_w;
        break;
    case mb_write_single_reg:
        modbus.mb_fun = mb_single_w;
        break;
    case mb_write_mult_reg:
        modbus.mb_fun = mb_hold_w;
        break;
    default:
        return -1;
        break;
    }
    modbus.slave_id = dev->sid;
    modbus.start_addr = addr;
    modbus.wBuf = data;
    modbus.rwLen = len;
    modbus.resBuf = rec_data;
    modbus.resLen = &rec_len;

    while (repeat_times--)
    {
        ret = xp_modbus_ask(port, &modbus);
        if (0 == ret)
            break;
    }

    if (0 != ret)
        return -1;
    return 0;
}

static int bus_485_sread(xp_modbus_device_t *dev, unsigned short addr, unsigned short *data)
{
    return bus_485_read(dev, addr, data, 1, mb_read_single_reg);
}

static int bus_485_swrite(xp_modbus_device_t *dev, unsigned short addr, unsigned short data)
{
    return bus_485_write(dev, addr, &data, 1, mb_write_single_reg);
}

int xp_modbus_485_constructor(struct xp_modbus_bus *bus)
{
    bus->read = bus_485_read;
    bus->write = bus_485_write;
    bus->sread = bus_485_sread;
    bus->swrite = bus_485_swrite;

    if (0 > bus_485_init(bus))
        return -1;
    return 0;
}
